package avicit.bdp.dgs.standard.service;

import avicit.bdp.approval.api.impl.StandardApprovalApi;
import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.dto.approval.ApprovalDTO;
import avicit.bdp.common.service.dto.CommonTypeDTO;
import avicit.bdp.common.service.service.CommonTypeService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.CheckParaUtils;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.DateUtils;
import avicit.bdp.common.utils.VersionUtil;
import avicit.bdp.common.utils.enums.CommonTypeEnums;
import avicit.bdp.core.constant.Constants;
import avicit.bdp.core.enums.DataElementDataTypeEnum;
import avicit.bdp.core.enums.DataElementStandardTypeEnum;
import avicit.bdp.core.enums.DataStateEnum;
import avicit.bdp.core.enums.Dimension;
import avicit.bdp.core.poi.util.csv.ExcelUtils;
import avicit.bdp.core.poi.util.csv.style.ExcelExportTitleStyle;
import avicit.bdp.common.service.service.InsertSysExcelImportLogService;
import avicit.bdp.dgs.standard.dao.DataElementDao;
import avicit.bdp.dgs.standard.dto.DataElementDto;
import avicit.bdp.dgs.standard.dto.DataElementHistoryDto;
import avicit.bdp.dgs.standard.dto.LookupTableDto;
import avicit.bdp.dgs.standard.vo.DataElementDistributionVo;
import avicit.bdp.dgs.standard.vo.DataElementHistoryVo;
import avicit.bdp.dgs.standard.vo.DataElementImportResult;
import avicit.bdp.dgs.standard.vo.StandardApprovalStaticsVo;
import avicit.bdp.dgs.standard.vo.StandardPieVo;
import avicit.bdp.dgs.standard.vo.StateLineChartDataVo;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.properties.PlatformConstant;
import avicit.platform6.core.rest.msg.QueryRespBean;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

/**
 * 数据元服务层
 *
 * @author mayi
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class DataElementService extends BaseService<DataElementDao, DataElementDto> {

  private static final Logger logger = LoggerFactory.getLogger(DataElementService.class);

  @Resource private DataElementDao dataElementDao;
  @Resource private DataElementHistoryService dataElementHistoryService;
  @Resource private CommonTypeService commonTypeService;
  @Resource private ConvertColumnClient convertColumnClient;
  @Resource private StandardApprovalApi standardApprovalRpc;
  @Resource private LookupTableService lookupTableService;
  @Resource private InsertSysExcelImportLogService insertSysExcelImportLog;

  public QueryRespBean<DataElementDto> getPageList(
      String typeId, String keyWords, Integer status, Integer pageNo, Integer pageSize)
      throws Exception {

    List<String> typeIdList = new ArrayList<>();
    if (StringUtils.isNotEmpty(typeId)) {
      commonTypeService.typeIdConvert(
          typeId, typeIdList, CommonTypeEnums.DGS_STANDARD_DATA_ELEMENT_TYPE);
    }

    QueryRespBean<DataElementDto> queryRespBean = new QueryRespBean<>();
    PageHelper.startPage(pageNo, pageSize);
    Page<DataElementDto> page = dataElementDao.getPageList(typeIdList, null, keyWords, status, ThreadContextHelper.getWordSecretList());
    try {
      valueConvert(page);
    } catch (Exception e) {
      e.printStackTrace();
    }
    queryRespBean.setResult(page);
    BdpLogUtil.log4Query(queryRespBean);
    return queryRespBean;
  }

  private void valueConvert(Page<DataElementDto> page) {
    // 循环组装请求数据
    Map<String, Set<String>> convertFormData = new HashMap<>(16);
    for (DataElementDto dataElementDto : page) {
      BusinessUtil.createConvertSet(
              convertFormData, SystemConstant.USER, dataElementDto.getCreatedBy());
      BusinessUtil.createConvertSet(
              convertFormData, Constants.BDP_DATA_SECRET_LEVEL_KEY, dataElementDto.getSecretLevel());
    }
    if (convertFormData.size() > 0) {
      // 获取请求结果
      Map<String, Map<String, String>> convertResultData =
          convertColumnClient.replace(convertFormData);
      // 循环设置Alias或Name的值
      for (DataElementDto dataElementDto : page) {
        fullEnumInfo(dataElementDto);
        dataElementDto.setCreatedBy(
            BusinessUtil.convertFormat(
                convertResultData, SystemConstant.USER, dataElementDto.getCreatedBy()));
        dataElementDto.setSecretLevelName(BusinessUtil.convertFormat(convertResultData, Constants.BDP_DATA_SECRET_LEVEL_KEY, dataElementDto.getSecretLevel()));
      }
    }
  }

  public String save(DataElementDto dataElementDto) {

    valid(dataElementDto);

    checkNameUnique(dataElementDto);

    checkCodeUnique(dataElementDto);

    dataElementDto.setId(ComUtil.getId());
    dataElementDto.setStatus(DataStateEnum.UNDER_EDITING.getCode());
    dataElementDto.setDataElementVersion(VersionUtil.generateVersion2Default());

    this.insert(dataElementDto);
    log4Insert(dataElementDto);

    // 新增历史记录
    dataElementHistoryService.insert(getDataElementHistoryDto(dataElementDto));

    return dataElementDto.getId();
  }

  public void update(DataElementDto dataElementDto) {

    valid(dataElementDto);

    DataElementDto oldDataElementDto = this.selectByPrimaryKey(dataElementDto.getId());
    CheckParaUtils.checkDbObjectValidity(
        oldDataElementDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", dataElementDto.getId()));

    if (oldDataElementDto.getStatus() != null
            && (oldDataElementDto.getStatus() == DataStateEnum.UNDER_APPROVAL.getCode()
                || oldDataElementDto.getStatus() == DataStateEnum.PUBLISHED.getCode())
        || oldDataElementDto.getStatus() == DataStateEnum.DEPRECATED.getCode()) {
      throw new BusinessException("该数据元正在审批中或已发布或已废止，不能编辑!");
    }

    // 校验标准名称唯一性
    if (!dataElementDto.getName().equals(oldDataElementDto.getName())) {
      checkNameUnique(dataElementDto);
    }

    // 校验标准编码唯一性
    if (!dataElementDto.getCode().equals(oldDataElementDto.getCode())) {
      checkCodeUnique(dataElementDto);
    }

    // 修改历史记录
    DataElementHistoryDto oldDataElementHistoryDto =
        dataElementHistoryService.selectByDataElementIdAndVersion(
            dataElementDto.getId(), dataElementDto.getDataElementVersion());

    CheckParaUtils.checkDbObjectValidity(
        oldDataElementHistoryDto,
        logger,
        String.format("没有查到数据，请确保id:%s的数据是否存在", dataElementDto.getId()));

    dataElementDto.setStatus(DataStateEnum.UNDER_EDITING.getCode());
    this.updateByPrimaryKey(dataElementDto);
    BdpLogUtil.log4Update(dataElementDto, oldDataElementDto);

    String oldDataElementHistoryId = oldDataElementHistoryDto.getId();
    BeanUtils.copyProperties(dataElementDto, oldDataElementHistoryDto);
    oldDataElementHistoryDto.setStatus(DataStateEnum.UNDER_EDITING.getCode());
    oldDataElementHistoryDto.setId(oldDataElementHistoryId);
    dataElementHistoryService.updateByPrimaryKeySelective(oldDataElementHistoryDto);
  }

  private void valid(DataElementDto dataElementDto) {
    CheckParaUtils.checkObjectValidity(dataElementDto.getCode(), logger, "编码不能为空");
    CheckParaUtils.checkStringValidity(dataElementDto.getName(), logger, "名称不能为空");
    CheckParaUtils.checkStringValidity(dataElementDto.getTypeId(), logger, "分类不能为空");
  }

  public Boolean batchDelete(String ids) {
    CheckParaUtils.checkStringValidity(ids, logger, "没有传入待删除的ID");
    String[] idArr = ids.split(Constants.COMMA);
    for (String id : idArr) {
      DataElementDto dataElementDto = this.selectByPrimaryKey(id);
      CheckParaUtils.checkDbObjectValidity(
          dataElementDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", id));

      // 审批中的数据，需要先撤回申请
      if (dataElementDto.getStatus() == DataStateEnum.UNDER_APPROVAL.getCode()) {
        throw new BusinessException("该数据元正在审批中，请先撤回!");
      }

      // todo 已经被引用的数据元，不允许直接删除，给出提示:该数据元已经被引用，不能删除

      // 历史版本如果有已发布的记录，不能删除
      int countStatus =
          dataElementHistoryService.countByStatus(id, DataStateEnum.PUBLISHED.getCode());
      if (countStatus > 0) {
        throw new BusinessException("该数据元有发布的版本存在，暂不能删除!");
      }

      this.mapper.deleteByPrimaryKey(id);

      // 删除所有历史版本
      dataElementHistoryService.deleteByDataElementId(id);

      // 记录日志
      BdpLogUtil.log4Delete(dataElementDto);
    }
    return true;
  }

  public DataElementDto queryById(String id) {
    DataElementDto dataElementDto = this.selectByPrimaryKey(id);
    CheckParaUtils.checkDbObjectValidity(
        dataElementDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", id));
    CommonTypeDTO commonTypeDTO = commonTypeService.selectByPrimaryKey(dataElementDto.getTypeId());
    if (commonTypeDTO != null) {
      dataElementDto.setTypeName(commonTypeDTO.getName());
    }
    if (StringUtils.isNotBlank(dataElementDto.getLookupTableId())) {
      LookupTableDto lookupTableDto =
          lookupTableService.selectByPrimaryKey(dataElementDto.getLookupTableId());
      if (lookupTableDto != null) {
        dataElementDto.setLookupTableName(lookupTableDto.getName());
      }
    }
    fullEnumInfo(dataElementDto);
    return dataElementDto;
  }

  private void fullEnumInfo(DataElementDto dataElementDto) {
    dataElementDto.setStatusString(DataStateEnum.getDescByCode(dataElementDto.getStatus()));
    dataElementDto.setDataTypeString(
        DataElementDataTypeEnum.getDescByCode(dataElementDto.getDataType()));
    dataElementDto.setStandardTypeString(
        DataElementStandardTypeEnum.getDescByCode(dataElementDto.getStandardType()));
  }

  /**
   * 校验名称唯一性
   *
   * @param dataElementDto 检测对象
   */
  private void checkNameUnique(DataElementDto dataElementDto) {
    if (dataElementDto != null) {
      if (StringUtils.isEmpty(dataElementDto.getName())) {
        throw new BusinessException("名称不能为空");
      }

      DataElementDto paramName = new DataElementDto();
      paramName.setName(dataElementDto.getName());
      if (this.selectCount(paramName) > 0) {
        throw new BusinessException("名称[" + dataElementDto.getName() + "]已存在");
      }
    }
  }

  /**
   * 校验名称和代码唯一性
   *
   * @param dataElementDto 检测对象
   */
  private void checkCodeUnique(DataElementDto dataElementDto) {
    if (dataElementDto != null) {
      if (StringUtils.isEmpty(dataElementDto.getCode())) {
        throw new BusinessException("代码不能为空");
      }

      DataElementDto paramCode = new DataElementDto();
      paramCode.setCode(dataElementDto.getCode());
      if (this.selectCount(paramCode) > 0) {
        throw new BusinessException("编码[" + dataElementDto.getCode() + "]已存在");
      }
    }
  }

  public QueryRespBean<DataElementHistoryVo> getVersionPage(
      String dataElementId, Integer pageNo, Integer pageSize) {
    QueryRespBean<DataElementHistoryVo> queryRespBean = new QueryRespBean<>();
    PageHelper.startPage(pageNo, pageSize);
    Page<DataElementHistoryVo> page = dataElementHistoryService.getVersionPage(dataElementId);
    for (DataElementHistoryVo dataElementHistoryVo : page) {
      dataElementHistoryVo.setStatusString(
          DataStateEnum.getDescByCode(dataElementHistoryVo.getStatus()));
    }
    queryRespBean.setResult(page);
    return queryRespBean;
  }

  /**
   * 获取版本
   *
   * @param dataElementId 数据元ID
   * @return 版本
   */
  public String getVersion(String dataElementId) {
    String maxVersion =
        this.dataElementHistoryService.searchMaxVersionByDataElementId(dataElementId);
    if (StringUtils.isEmpty(maxVersion)) {
      return VersionUtil.generateVersion2Default();
    }

    return VersionUtil.generateVersion2Next(maxVersion);
  }

  public Boolean publish(String[] ids) {
    if (ids != null && ids.length > 0) {
      List<DataElementDto> updateList = new ArrayList<>(ids.length);

      boolean isDataElementAudit =
          ConfigUtils.getInstance().getBoolean("dgs", "isDataElementAudit", true);

      if (isDataElementAudit) {

        List<ApprovalDTO> approvalList = new ArrayList<>(ids.length);
        for (String id : ids) {
          DataElementDto dataElement = this.selectByPrimaryKey(id);

          if (dataElement.getStatus() == DataStateEnum.PUBLISHED.getCode()
              || dataElement.getStatus() == DataStateEnum.UNDER_APPROVAL.getCode()
              || dataElement.getStatus() == DataStateEnum.DEPRECATED.getCode()) {
            throw new BusinessException(
                "数据元【" + dataElement.getName() + "】在状态为审批中或已发布或已废止，不能执行发布操作!");
          }

          dataElement.setStatus(DataStateEnum.UNDER_APPROVAL.getCode());

          updateList.add(dataElement);

          ApprovalDTO approvalDTO = new ApprovalDTO();
          approvalDTO.setBusinessId(id);
          approvalDTO.setBusinessName(dataElement.getName());
          approvalDTO.setBusinessVersion(dataElement.getDataElementVersion());
          approvalList.add(approvalDTO);
        }

        // 向审批中心发送发布请求
        Boolean releaseResult = standardApprovalRpc.release(approvalList);
        if (releaseResult && CollectionUtils.isNotEmpty(updateList)) {
          if (this.mapper.batchUpdate(updateList) > 0) {
            return dataElementHistoryService.batchUpdateStatus(
                generateUpdateHistoryDto(updateList));
          }
        }
      } else {
        for (String id : ids) {
          DataElementDto dataElement = this.selectByPrimaryKey(id);

          if (dataElement.getStatus() == DataStateEnum.PUBLISHED.getCode()
              || dataElement.getStatus() == DataStateEnum.UNDER_APPROVAL.getCode()
              || dataElement.getStatus() == DataStateEnum.DEPRECATED.getCode()) {
            throw new BusinessException(
                "数据元【" + dataElement.getName() + "】在状态为审批中或已发布或已废止，不能执行发布操作!");
          }

          dataElement.setStatus(DataStateEnum.PUBLISHED.getCode());
          updateList.add(dataElement);
        }

        if (CollectionUtils.isNotEmpty(updateList)) {
          if (this.mapper.batchUpdate(updateList) > 0) {
            return dataElementHistoryService.batchUpdateStatus(
                generateUpdateHistoryDto(updateList));
          }
        }
      }

      return false;
    }

    return false;
  }

  /** 接收审批中心通知 */
  public boolean receiveMsgFromApprovalCenter(List<ApprovalDTO> approvalList) {
    if (CollectionUtils.isEmpty(approvalList)) {
      return true;
    }
    List<DataElementDto> updateList = new ArrayList<>(approvalList.size());

    for (ApprovalDTO approval : approvalList) {
      if (approval == null) {
        continue;
      }
      if (StringUtils.isAnyEmpty(approval.getBusinessId(), approval.getBusinessVersion())) {
        continue;
      }
      if (approval.getApprovalResultType() == null) {
        continue;
      }

      DataElementDto dataElementDto = this.selectByPrimaryKey(approval.getBusinessId());
      if (dataElementDto == null) {
        continue;
      }

      if (approval.getApprovalResultType().equals(0)) {
        // 审批通过，设置为“已发布”状态
        dataElementDto.setStatus(DataStateEnum.PUBLISHED.getCode());
      } else if (approval.getApprovalResultType().equals(1)) {
        // 审批不通过通过，设置为“已驳回”状态
        dataElementDto.setStatus(DataStateEnum.REJECTED.getCode());
      } else {
        continue;
      }

      dataElementDto.setDataElementVersion(approval.getBusinessVersion());
      updateList.add(dataElementDto);
    }

    if (CollectionUtils.isNotEmpty(updateList)) {
      int updateNum = this.mapper.batchUpdate(updateList);
      if (updateNum > 0) {
        return dataElementHistoryService.batchUpdateStatus(generateUpdateHistoryDto(updateList));
      }
    }

    return false;
  }

  public void offline(String id) {
    DataElementHistoryDto historyDTO = this.dataElementHistoryService.selectByPrimaryKey(id);
    if (historyDTO == null) {
      throw new BusinessException("根据ID未找到历史版本，id=" + id + "，更新失败");
    }

    if (historyDTO.getStatus() != null
        && historyDTO.getStatus() != DataStateEnum.PUBLISHED.getCode()) {
      throw new BusinessException("该数据元不在已发布状态，不能执行废止操作!");
    }

    // todo 存在引用关系的，不能下线

    historyDTO.setStatus(DataStateEnum.DEPRECATED.getCode());
    this.dataElementHistoryService.updateByPrimaryKeySelective(historyDTO);

    DataElementDto dataElementDto = this.selectByPrimaryKey(historyDTO.getDataElementId());
    if (historyDTO
        .getDataElementVersion()
        .equalsIgnoreCase(dataElementDto.getDataElementVersion())) {
      dataElementDto.setStatus(DataStateEnum.DEPRECATED.getCode());
      this.updateByPrimaryKey(dataElementDto);
    }

    String logTitle = "废止数据项标准：【" + JSONObject.toJSONString(historyDTO) + "】";
    BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.update);
  }

  /**
   * 创建历史版本数据
   *
   * @param list list
   */
  private List<DataElementHistoryDto> generateUpdateHistoryDto(List<DataElementDto> list) {
    List<DataElementHistoryDto> historyDtoList = new ArrayList<>();
    if (CollectionUtils.isNotEmpty(list)) {
      for (DataElementDto dataElementDto : list) {
        DataElementHistoryDto historyDTO = getUpdateDataElementHistoryDto(dataElementDto);
        historyDtoList.add(historyDTO);
      }
    }

    return historyDtoList;
  }

  private DataElementHistoryDto getUpdateDataElementHistoryDto(DataElementDto dataElementDto) {
    DataElementHistoryDto historyDTO = new DataElementHistoryDto();
    BeanUtils.copyProperties(dataElementDto, historyDTO);
    historyDTO.setId(null);
    historyDTO.setDataElementId(dataElementDto.getId());
    historyDTO.setDataElementVersion(dataElementDto.getDataElementVersion());
    historyDTO.setStatus(dataElementDto.getStatus());
    return historyDTO;
  }

  private DataElementHistoryDto getDataElementHistoryDto(DataElementDto dataElementDto) {
    DataElementHistoryDto historyDTO = new DataElementHistoryDto();
    BeanUtils.copyProperties(dataElementDto, historyDTO);
    historyDTO.setId(ComUtil.getId());
    historyDTO.setDataElementId(dataElementDto.getId());
    historyDTO.setDataElementVersion(dataElementDto.getDataElementVersion());
    historyDTO.setStatus(dataElementDto.getStatus());
    return historyDTO;
  }

  public String amend(DataElementDto dataElementDto) {
    valid(dataElementDto);

    DataElementDto oldDataElementDto = this.selectByPrimaryKey(dataElementDto.getId());
    CheckParaUtils.checkDbObjectValidity(
        oldDataElementDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", dataElementDto.getId()));

    if (oldDataElementDto.getStatus() != null
        && oldDataElementDto.getStatus() == DataStateEnum.UNDER_APPROVAL.getCode()) {
      throw new BusinessException("该数据元正在审批中，不能修订!");
    }

    // 校验标准名称唯一性
    if (!dataElementDto.getName().equals(oldDataElementDto.getName())) {
      checkNameUnique(dataElementDto);
    }

    // 校验标准编码唯一性
    if (!dataElementDto.getCode().equals(oldDataElementDto.getCode())) {
      checkCodeUnique(dataElementDto);
    }

    dataElementDto.setStatus(DataStateEnum.UNDER_EDITING.getCode());
    dataElementDto.setDataElementVersion(getVersion(dataElementDto.getId()));

    this.updateByPrimaryKey(dataElementDto);
    BdpLogUtil.log4Update(dataElementDto, oldDataElementDto);

    // 新增历史记录
    dataElementHistoryService.insert(getDataElementHistoryDto(dataElementDto));

    return dataElementDto.getId();
  }

  public Boolean revoke(String[] ids) {
    if (ids != null && ids.length > 0) {
      List<ApprovalDTO> approvalList = new ArrayList<>(ids.length);
      List<DataElementDto> updateList = new ArrayList<>(ids.length);

      for (String id : ids) {
        DataElementDto dataElement = this.selectByPrimaryKey(id);

        DataElementDto dataElementDto = this.selectByPrimaryKey(id);
        CheckParaUtils.checkDbObjectValidity(
            dataElementDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", id));

        if (dataElementDto.getStatus() != null
            && dataElementDto.getStatus() != DataStateEnum.UNDER_APPROVAL.getCode()) {
          throw new BusinessException("该数据元不在审批状态，无能撤回!");
        }

        dataElement.setStatus(DataStateEnum.UNDER_EDITING.getCode());
        updateList.add(dataElement);

        ApprovalDTO approvalDTO = new ApprovalDTO();
        approvalDTO.setBusinessId(id);
        approvalDTO.setBusinessName(dataElement.getName());
        approvalDTO.setBusinessVersion(dataElement.getDataElementVersion());
        approvalList.add(approvalDTO);
      }

      // 向审批中心发送撤回请求
      Boolean releaseResult = standardApprovalRpc.revoke(approvalList);
      if (releaseResult && CollectionUtils.isNotEmpty(updateList)) {
        if (this.mapper.batchUpdate(updateList) > 0) {
          return dataElementHistoryService.batchUpdateStatus(generateUpdateHistoryDto(updateList));
        }
      }

      String logTitle = "撤回数据项标准：【" + JSONObject.toJSONString(ids) + "】";
      BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.update);
      return false;
    }
    return false;
  }

  /**
   * 查询数据元
   *
   * @param projectId projectId
   * @param keyWords 关键词
   * @return 树状数据
   */
  public List<CommonTypeDTO> getTreeList(String projectId, String keyWords) {
    try {
      // 顶级节点
      List<CommonTypeDTO> result = new ArrayList<>();
      // 子节点数据
      Map<String, List<CommonTypeDTO>> map = new HashMap<>();
      List<CommonTypeDTO> commonTypeList;

      // 项目空间通用树
      CommonTypeDTO commonTypeDTO = new CommonTypeDTO();
      commonTypeDTO.setType(CommonTypeEnums.DGS_STANDARD_DATA_ELEMENT_TYPE);
      if (StringUtils.isNotEmpty(projectId)) {
        commonTypeDTO.setProjectId(projectId);
      }

      List<CommonTypeDTO> childrenType = commonTypeService.selectList(commonTypeDTO);
      commonTypeList =
          Stream.of(childrenType)
              .flatMap(Collection::stream)
              .distinct()
              .sorted(
                  Comparator.comparing(
                      CommonTypeDTO::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
              .collect(Collectors.toList());

      String rootTypeId = "";
      if (CollectionUtils.isNotEmpty(commonTypeList)) {
        for (CommonTypeDTO commonType : commonTypeList) {
          if ("-1".equals(commonType.getParentId())) {
            result.add(commonType);
            rootTypeId = commonType.getId();
          } else {
            if (map.containsKey(commonType.getParentId())) {
              map.get(commonType.getParentId()).add(commonType);
            } else {
              List<CommonTypeDTO> commonTableDTOList = new ArrayList<>();
              commonTableDTOList.add(commonType);
              map.put(commonType.getParentId(), commonTableDTOList);
            }
          }
        }
      }

      List<String> typeIds =
          commonTypeService.getChildrenIds(
              CommonTypeEnums.DGS_STANDARD_DATA_ELEMENT_TYPE, rootTypeId, projectId);
      if (typeIds == null) {
        typeIds = Lists.newArrayList();
      }
      typeIds.add(rootTypeId);
      List<DataElementDto> dataElementList = this.mapper.getPageList(typeIds, null, keyWords, null, null);
      buildChildren(result, map, dataElementList);

      // 顶级节点不必返回
      if (result.size() == 1) {
        result = result.get(0).getChildren();
      }
      return result;
    } catch (Exception e) {
      logger.error("查询数据元异常{}", e.getMessage());
      throw new BusinessException(e.getMessage(), e);
    }
  }

  private void buildChildren(
      List<CommonTypeDTO> list,
      Map<String, List<CommonTypeDTO>> map,
      List<DataElementDto> dataElementList) {

    for (CommonTypeDTO commonTypeDTO : list) {
      List<CommonTypeDTO> commonTypeList = map.get(commonTypeDTO.getId());
      if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(commonTypeList)) {
        buildChildren(commonTypeList, map, dataElementList);
        commonTypeDTO.setChildren(commonTypeList);
      } else {
        List<CommonTypeDTO> commonTypeDtoList = new ArrayList<>();
        // 叶子节点增加表数据到树
        if (CollectionUtils.isNotEmpty(dataElementList)) {
          for (DataElementDto dataElement : dataElementList) {
            if (commonTypeDTO.getId().equals(dataElement.getTypeId())) {
              CommonTypeDTO commonType = new CommonTypeDTO();
              commonType.setId(dataElement.getId());
              commonType.setName(dataElement.getName());
              commonType.setRemark(dataElement.getName());
              commonType.setSelectable(true);
              commonTypeDtoList.add(commonType);
            }
          }
        }

        commonTypeDTO.setChildren(commonTypeDtoList);
      }
    }
  }

  public void downloadTemplate(HttpServletResponse response) throws Exception {
    ExcelUtils excelUtils = new ExcelUtils();
    List<DataElementDto> dataList = new ArrayList<>();

    DataElementDto dto = new DataElementDto();
    dto.setName("数据项中文名称");
    dto.setEnName("数据项英文名称");
    dto.setCode("standard_code");
    dto.setFullTypeName("分类1/分类2/分类3");
    dto.setDataType(DataElementDataTypeEnum.BIGINT.getCode());
    dto.setStandardType(DataElementStandardTypeEnum.ENTERPRISE_STANDARDS.getCode());
    dto.setStandardFrom("标准来源");
    dto.setBusinessMeaning("标准业务含义");
    dto.setLength(10);
    dto.setDecimalPrecision(2);
    dto.setLookupTableCode("lookup_table_code");
    dto.setNotNull(1);
    dto.setUniqueKey(1);
    dataList.add(dto);

    // 设置导出参数
    ExportParams params = new ExportParams();
    // 设置excel类型，XSSF代表xlsx，HSSF代表xls
    params.setType(ExcelType.XSSF);
    params.setStyle(ExcelExportTitleStyle.class);
    excelUtils.baseExport(dataList, DataElementDto.class, "数据项导入模板", params, response);

    String logTitle = "下载数据项标准模板";
    BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.select);
  }

  public void export(String typeId, List<String> idList, HttpServletResponse response) {
    ExcelUtils excelUtils = new ExcelUtils();

    try {
      List<String> typeIds = new ArrayList<>();
      if (StringUtils.isNotEmpty(typeId)) {
        commonTypeService.typeIdConvert(
            typeId, typeIds, CommonTypeEnums.DGS_STANDARD_DATA_ELEMENT_TYPE);
      }

      List<DataElementDto> dataList = this.mapper.getPageList(typeIds, idList, null, null, null);
      List<String> typeIdList =
          dataList.stream().map(DataElementDto::getTypeId).collect(Collectors.toList());
      Map<String, String> fullPathNameMap = commonTypeService.getFullPathNameByIds(typeIdList);
      for (DataElementDto dataElementDto : dataList) {
        if (StringUtils.isNotBlank(dataElementDto.getLookupTableId())) {
          LookupTableDto lookupTableDto =
              lookupTableService.selectByPrimaryKey(dataElementDto.getLookupTableId());
          if (lookupTableDto != null) {
            dataElementDto.setLookupTableCode(lookupTableDto.getCode());
          }
        }
        if (StringUtils.isNotBlank(dataElementDto.getTypeId())) {
          dataElementDto.setFullTypeName(fullPathNameMap.get(dataElementDto.getTypeId()));
        }
      }
      // 设置导出参数
      ExportParams params = new ExportParams();
      // 设置excel类型，XSSF代表xlsx，HSSF代表xls
      params.setType(ExcelType.XSSF);
      params.setStyle(ExcelExportTitleStyle.class);
      logger.info("开始导出.....");
      excelUtils.baseExport(dataList, DataElementDto.class, "数据项", params, response);

      String logTitle = "导出数据项标准";
      BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.select);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public String importDataElement(MultipartFile file) throws Exception {
    CheckParaUtils.checkObjectValidity(file, logger, "参数[file]不能为空");

    int successNum = 0;
    int errorNum = 0;
    List<DataElementDto> errorList = new ArrayList<>();

    ExcelUtils excelUtils = new ExcelUtils();
    List<DataElementDto> dataList = excelUtils.importExcel(file, 0, 1, false, DataElementDto.class);
    logger.info("解析excel文件数据成功，数据有[{}]条", dataList.size());
    if (CollectionUtils.isNotEmpty(dataList)) {
      Map<String, String> fullTypeNameMap = new HashMap<>();
      for (DataElementDto data : dataList) {
        String fullTypeName = data.getFullTypeName();
        if (StringUtils.isNotBlank(data.getFullTypeName())) {

          handleType(fullTypeNameMap, data, fullTypeName);

          if (StringUtils.isEmpty(data.getTypeId())) {
            errorNum++;
            data.setErrorMsg("分类路径不存在");
            errorList.add(data);
            logger.info("分类路径不存在");
            continue;
          }

          try {
            if (StringUtils.isNotBlank(data.getLookupTableCode())) {
              String lookupTableId = lookupTableService.getIdByCode(data.getLookupTableCode());
              if (StringUtils.isNotBlank(lookupTableId)) {
                data.setQuoteLookupTable(Constants.STATUS_ONE);
                data.setLookupTableId(lookupTableId);
              } else {
                errorNum++;
                data.setErrorMsg("值域标准代码不存在");
                errorList.add(data);
                continue;
              }
            }
            save(data);
            successNum++;
          } catch (Exception e) {
            errorNum++;
            data.setErrorMsg(e.getMessage());
            errorList.add(data);
          }
        } else {
          errorNum++;
          data.setErrorMsg("分类路径不能为空");
          errorList.add(data);
        }
      }

      String errorImportFilePath = "";
      if (CollectionUtils.isNotEmpty(errorList)) {
        List<DataElementImportResult> errorList2 =
            errorList.stream()
                .map(
                    item -> {
                      DataElementImportResult result = new DataElementImportResult();
                      BeanUtils.copyProperties(item, result);
                      return result;
                    })
                .collect(Collectors.toList());

        // 生成错误信息excel
        // 错误文件存储路径
        String dirPath = ConfigUtils.getInstance().getString("system.data.path") + "errorFile/";
        ExportParams params = new ExportParams();
        params.setType(ExcelType.XSSF);
        params.setStyle(ExcelExportTitleStyle.class);
        errorImportFilePath =
            excelUtils.exportExcel(
                errorList2,
                DataElementImportResult.class,
                "dataElementImportError_" + System.currentTimeMillis(),
                params,
                dirPath);
        logger.info("errorImportFilePath = {}", errorImportFilePath);
      }

      insertSysExcelImportLog.insertSysExcelImportLog(
          errorImportFilePath,
          dataList.size(),
          successNum,
          errorNum,
          "DataElementImport",
          "bdp_dgs_data_element");
    }

    String logTitle = String.format("总共导入%d条，成功%s条,失败%d条。", dataList.size(), successNum, errorNum);
    BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.insert);
    return logTitle;
  }

  private void handleType(
      Map<String, String> fullTypeNameMap, DataElementDto data, String fullTypeName) {
    String typeId = fullTypeNameMap.get(fullTypeName);
    if (StringUtils.isNotBlank(typeId)) {
      data.setTypeId(typeId);
    } else {
      typeId =
          commonTypeService.getTypeIdByFullName(
              data.getFullTypeName(), typeId, CommonTypeEnums.DGS_STANDARD_DATA_ELEMENT_TYPE);
      if (StringUtils.isNotBlank(typeId)) {
        fullTypeNameMap.put(fullTypeName, typeId);
        data.setTypeId(typeId);
      }
    }
  }

  public List<StandardPieVo> statusStatics() {
    List<StandardPieVo> result = new ArrayList<>();
    DataElementDto param = new DataElementDto();
    int total = this.mapper.selectCount(param);
    if (total > 0) {
      result = mapper.statusStatics();
      if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(result)) {
        for (StandardPieVo standardPieVo : result) {
          standardPieVo.setStatusStr(DataStateEnum.getDescByCode(standardPieVo.getStatus()));
          standardPieVo.setPercent(standardPieVo.getTotalNum() / (double) total * 100);
        }
      }
    }
    return result;
  }

  /**
   * 概览图表数据
   *
   * @param dimension 统计维度 [week,month,year,selfDefine] week:表示近7天 month:表示近30天 year:表示近1年
   *     userDefine：自定义时间段，开始时间和结束时间值不能为空
   * @param startTime 开始日期
   * @param endTime 结束日期 按天统计：DATE_FORMAT(start_time,'%Y%m%d') 按周统计：DATE_FORMAT(start_time,'%Y%u')
   *     按月统计：DATE_FORMAT(start_time,'%Y%m')
   * @return 数据湖概览图表数据
   */
  public StateLineChartDataVo lineChart(String dimension, String startTime, String endTime) {

    Dimension dimensionByName = Dimension.getDimensionByName(dimension);
    if (dimensionByName == null) {
      throw new BusinessException("请检查统计维度参数");
    }

    StateLineChartDataVo dataVo = new StateLineChartDataVo();
    // 赋值日期值
    if (Dimension.USER_DEFINE != dimensionByName) {
      startTime = DateUtils.getDayNumBeforeToday(dimensionByName.getDays());
      endTime = DateUtils.getDayNumBeforeToday(Dimension.DAY.getDays());
    }

    // 计算开始时间和结束时间的间隔天数
    List<String> dateList;
    // 通过维度和统计时间计算数据库日期统计格式
    String pattern = Dimension.DAY.getPattern();
    if (Dimension.YEAR == dimensionByName) {
      // 选择近1年，按照月统计
      pattern = Dimension.MONTH.getPattern();
      dateList = DateUtils.getAllMonths(startTime, endTime);
    } else {
      dateList = DateUtils.getAllDays(startTime, endTime);
    }

    List<Integer> publishedList = new ArrayList<>();
    List<Integer> rejectedList = new ArrayList<>();

    for (int i = 0, j = dateList.size(); i < j; i++) {
      publishedList.add(null);
      rejectedList.add(null);
    }

    int endIndex = 10;
    if (Dimension.WEEK == dimensionByName) {
      endIndex = 10;
    } else if (Dimension.MONTH == dimensionByName) {
      endIndex = 10;
    } else if (Dimension.YEAR == dimensionByName) {
      endIndex = 7;
    }

    List<StandardApprovalStaticsVo> stateLineChartData =
        mapper.getStateLineChartData(pattern, startTime, endTime, endIndex);
    if (CollectionUtils.isNotEmpty(stateLineChartData)) {
      for (StandardApprovalStaticsVo vo : stateLineChartData) {
        String statDate = vo.getStatDate();
        int index = dateList.indexOf(statDate);
        if (vo.getState() == 1) {
          publishedList.set(index, vo.getTotal());
        } else {
          rejectedList.set(index, vo.getTotal());
        }
      }
    }

    if (Dimension.YEAR != dimensionByName) {
      List<String> list = new ArrayList<>();
      for (String date : dateList) {
        list.add(date.substring(date.indexOf(Constants.SUBTRACT_STRING) + 1));
      }
      dataVo.setDateList(list);
    } else {
      dataVo.setDateList(dateList);
    }
    dataVo.setPublishedList(publishedList);
    dataVo.setRejectedList(rejectedList);
    return dataVo;
  }

  public QueryRespBean<DataElementDistributionVo> getDistributionList(
      String dataElementId,
      String dataSourceId,
      String metadataId,
      String keyWords,
      Integer pageNo,
      Integer pageSize) {
    QueryRespBean<DataElementDistributionVo> queryRespBean = new QueryRespBean<>();
    PageHelper.startPage(pageNo, pageSize);
    Page<DataElementDistributionVo> page =
        dataElementDao.getDistributionList(dataElementId, dataSourceId, metadataId, keyWords);
    queryRespBean.setResult(page);
    return queryRespBean;
  }

  public Boolean dataElementDistribute(String dataElementId, List<String> metadataColumnList) {
    String logTitle = "标准下发：【" + JSONObject.toJSONString(dataElementId) + "】";
    BdpLogUtil.log("数据项标准模块", logTitle, PlatformConstant.OpType.insert);
    return dataElementDao.dataElementDistribute(dataElementId, metadataColumnList) > 0;
  }

  public Boolean dataElementDistributeRemove(
      String dataElementId, List<String> metadataColumnList) {
    return dataElementDao.dataElementDistributeRemove(dataElementId, metadataColumnList) > 0;
  }
}
